/////////////////////////////////////////////////////////////////////////////
// Name:        ax_ui_adaptation_standard.cpp
// Purpose:     Voicing adaptation for standard wxWidgets controls
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"
#include "wx/module.h"

#include "wx/stattext.h"
#include "wx/button.h"
#include "wx/checkbox.h"
#include "wx/radiobut.h"
#include "wx/listbox.h"
#include "wx/checklst.h"
#include "wx/choice.h"
#include "wx/combobox.h"
#include "wx/spinctrl.h"
#include "wx/tglbtn.h"
#include "wx/listctrl.h"
#include "wx/treectrl.h"
#include "wx/slider.h"
#include "wx/html/htmlwin.h"
#include "wx/notebook.h"
#include "wx/listbook.h"
#include "wx/treebook.h"
#include "wx/choicebk.h"
#include "wx/hyperlink.h"
#include "wx/dialog.h"
#include "wx/frame.h"

#include "ax/ax_ui_adaptation_standard.h"
#include "ax/ax_speech.h"
#include "ax/ax_utils.h"
#include "ax/ax_view.h"

#include "shortcutcust/shortcutcust.h"

IMPLEMENT_DYNAMIC_CLASS(AxStandardControlsVoiceAdaptationHandler, AxVoiceAdaptationHandler)

bool AxStandardControlsVoiceAdaptationHandler::CanHandleWindow(wxWindow* win)
{
    wxDialog* dialog = wxDynamicCast(win, wxDialog);
    wxPanel* panel = wxDynamicCast(win, wxPanel);
    wxFrame* frame = wxDynamicCast(win, wxFrame);
    wxButton* buttonCtrl = wxDynamicCast(win, wxButton);
    wxCheckBox* checkBoxCtrl = wxDynamicCast(win, wxCheckBox);
    wxRadioButton* radioButtonCtrl = wxDynamicCast(win, wxRadioButton);
    wxListBox* listBoxCtrl = wxDynamicCast(win, wxListBox);
    wxComboBox* comboBoxCtrl = wxDynamicCast(win, wxComboBox);
    wxChoice* choiceCtrl = wxDynamicCast(win, wxChoice);
    wxSpinCtrl* spinCtrl = wxDynamicCast(win, wxSpinCtrl);
    wxToggleButton* toggleCtrl = wxDynamicCast(win, wxToggleButton);
    wxListCtrl* listCtrl = wxDynamicCast(win, wxListCtrl);
    wxTreeCtrl* treeCtrl = wxDynamicCast(win, wxTreeCtrl);
    wxSlider* sliderCtrl = wxDynamicCast(win, wxSlider);
    wxHtmlWindow* htmlCtrl = wxDynamicCast(win, wxHtmlWindow);
    wxBookCtrlBase* notebookCtrl = wxDynamicCast(win, wxBookCtrlBase);
    wxHyperlinkCtrl* hyperlinkCtrl = wxDynamicCast(win, wxHyperlinkCtrl);
    wxStaticText* staticTextCtrl = wxDynamicCast(win, wxStaticText);
    wxStaticBox* staticBoxCtrl = wxDynamicCast(win, wxStaticBox);

    if (dialog || buttonCtrl || checkBoxCtrl || radioButtonCtrl || listBoxCtrl ||
        comboBoxCtrl || choiceCtrl || spinCtrl || toggleCtrl ||
        listCtrl || treeCtrl || sliderCtrl || htmlCtrl ||
        notebookCtrl || hyperlinkCtrl || frame || panel ||
        staticTextCtrl || staticBoxCtrl)
        return true;

    return false;
}

AxAdapter* AxStandardControlsVoiceAdaptationHandler::CreateEventHandler(wxWindow* win)
{
    wxDialog* dialog = wxDynamicCast(win, wxDialog);
    wxPanel* panel = wxDynamicCast(win, wxPanel);
    wxFrame* frame = wxDynamicCast(win, wxFrame);
    wxButton* buttonCtrl = wxDynamicCast(win, wxButton);
    wxCheckBox* checkBoxCtrl = wxDynamicCast(win, wxCheckBox);
    wxRadioButton* radioButtonCtrl = wxDynamicCast(win, wxRadioButton);
    wxListBox* listBoxCtrl = wxDynamicCast(win, wxListBox);
    wxComboBox* comboBoxCtrl = wxDynamicCast(win, wxComboBox);
    wxChoice* choiceCtrl = wxDynamicCast(win, wxChoice);
    wxSpinCtrl* spinCtrl = wxDynamicCast(win, wxSpinCtrl);
    wxToggleButton* toggleCtrl = wxDynamicCast(win, wxToggleButton);
    wxListCtrl* listCtrl = wxDynamicCast(win, wxListCtrl);
    wxTreeCtrl* treeCtrl = wxDynamicCast(win, wxTreeCtrl);
    wxSlider* sliderCtrl = wxDynamicCast(win, wxSlider);
    wxHtmlWindow* htmlCtrl = wxDynamicCast(win, wxHtmlWindow);
    wxBookCtrlBase* notebookCtrl = wxDynamicCast(win, wxBookCtrlBase);
    wxHyperlinkCtrl* hyperlinkCtrl = wxDynamicCast(win, wxHyperlinkCtrl);
    wxStaticText* staticTextCtrl = wxDynamicCast(win, wxStaticText);
    wxStaticBox* staticBoxCtrl = wxDynamicCast(win, wxStaticBox);

    if (dialog)
        return new AxDialogAdapter;
    else if (frame)
        return new AxFrameAdapter;
    else if (buttonCtrl)
        return new AxButtonAdapter;
    else if (checkBoxCtrl)
        return new AxCheckBoxAdapter;
    else if (radioButtonCtrl)
        return new AxRadioButtonAdapter;
    else if (listBoxCtrl)
        return new AxListBoxAdapter;
    else if (choiceCtrl)
        return new AxChoiceAdapter;
    else if (comboBoxCtrl)
        return new AxComboBoxAdapter;
    else if (spinCtrl)
        return new AxSpinCtrlAdapter;
    else if (toggleCtrl)
        return new AxToggleButtonAdapter;
    else if (listCtrl)
        return new AxListCtrlAdapter;
    else if (treeCtrl)
        return new AxTreeCtrlAdapter;
    else if (sliderCtrl)
        return new AxSliderAdapter;
    else if (htmlCtrl)
        return new AxHtmlWindowAdapter;
    else if (notebookCtrl)
        return new AxNotebookAdapter;
    else if (hyperlinkCtrl)
        return new AxHyperlinkCtrlAdapter;
    else if (staticTextCtrl)
        return new AxStaticTextAdapter;
    else if (staticBoxCtrl)
        return new AxStaticBoxAdapter;
    else if (panel)
        return new AxPanelAdapter;

    return NULL;    
}

// Override to adapt e.g. a composite control.
bool AxStandardControlsVoiceAdaptationHandler::Adapt(wxWindow* window, AxSpeech* speech, wxShortcutManager* shortcuts, wxArrayInt& ids)
{
    return AxVoiceAdaptationHandler::Adapt(window, speech, shortcuts, ids);
}

// Override to unadapt e.g. a composite control.
bool AxStandardControlsVoiceAdaptationHandler::Unadapt(wxWindow* window, wxArrayInt& ids)
{
    return AxVoiceAdaptationHandler::Unadapt(window, ids);
}

/// Override to prevent recursive adaptation, e.g. into a composite.
bool AxStandardControlsVoiceAdaptationHandler::CanRecursivelyAdapt(wxWindow* WXUNUSED(window)) const
{
    return true;
}

// Voiced dialog event handler

IMPLEMENT_DYNAMIC_CLASS(AxDialogAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxDialogAdapter, AxAdapter)
    EVT_INIT_DIALOG(AxDialogAdapter::OnInitDialog)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxDialogAdapter::GetName()
{
    return _("dialog");
}

/// Override to return the label for the control.
wxString AxDialogAdapter::GetLabel()
{
    if (GetDialog())
        return GetDialog()->GetTitle();
    else
        return wxEmptyString;
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxDialogAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetDialog())
    {
        wxString label = GetLabel();
        if (!label.IsEmpty())
            text << label << wxT(".");

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            text << wxT(" ") << content;
        }
    }
    return text;
}

// Get content
wxString AxDialogAdapter::GetContent(AxContentLevel level)
{
    wxString text;
    wxDialog* dialog = GetDialog();
    if (dialog)
    {
        // Get a summary of the dialog.
        if (level == AxContentFull)
        {
            text << _("These are the dialog controls:") << wxT(" ");

            wxWindowList::compatibility_iterator node = dialog->GetChildren().GetFirst();
            while (node)
            {
                wxWindow *child = node->GetData();
                DescribeWindowRecursively(child, AxContentSummary, text);

                node = node->GetNext();
            }        
        }
    }
    return text;
}

void AxDialogAdapter::OnInitDialog(wxInitDialogEvent& event)
{
    event.Skip();

    if (!GetDialog() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(_("showing dialog"));
    wxString label = GetLabel();
    if (!label.IsEmpty())
        text << wxT(" ") << label;

    // Don't purge this speech command with a subsequent focus command
    Say(text, AxSpeechFlagsInviolate|AxSpeechFlagsPurge);
}

wxDialog* AxDialogAdapter::GetDialog() const
{
    return wxDynamicCast(GetWindow(), wxDialog);
}

// Apply window colours
bool AxDialogAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            // Don't set the panel colour unless absolutely
            // necessary, since it can mess up standard behaviour.
            if (AxViewManager::Get()->GetVisualSettings().GetPanelColour().Ok())
                GetWindow()->SetBackgroundColour(AxViewManager::Get()->GetVisualSettings().GetBestPanelColour());
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}

// Panel handler

IMPLEMENT_DYNAMIC_CLASS(AxPanelAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxPanelAdapter, AxAdapter)
END_EVENT_TABLE()

/// Called after children have been updated
bool AxPanelAdapter::PostUpdateAppearance(int WXUNUSED(hints))
{
    if (GetPanel())
        GetPanel()->Layout();

    return true;
}

// Apply window colours
bool AxPanelAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            // Don't set the panel colour unless absolutely
            // necessary, since it can mess up standard behaviour.
            if (AxViewManager::Get()->GetVisualSettings().GetPanelColour().Ok())
                GetWindow()->SetBackgroundColour(AxViewManager::Get()->GetVisualSettings().GetBestPanelColour());
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}

wxPanel* AxPanelAdapter::GetPanel() const
{
    return wxDynamicCast(GetWindow(), wxPanel);
}

IMPLEMENT_DYNAMIC_CLASS(AxFrameAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxFrameAdapter, AxAdapter)
    EVT_MENU_OPEN(AxFrameAdapter::OnMenuOpen)
    EVT_MENU_CLOSE(AxFrameAdapter::OnMenuClose)
    EVT_MENU_HIGHLIGHT(wxID_ANY, AxFrameAdapter::OnMenuHighlight)
    EVT_MENU(wxID_ANY, AxFrameAdapter::OnMenuSelect)
    EVT_TIMER(wxID_ANY, AxFrameAdapter::OnTimeOut)
END_EVENT_TABLE()

AxFrameAdapter::~AxFrameAdapter()
{
    if (m_timer.IsRunning())
        m_timer.Stop();
}

void AxFrameAdapter::Init()
{
    m_openMenuPending = -1;

    m_timer.SetOwner(this);
}

// Override to return the name of the control.
wxString AxFrameAdapter::GetName()
{
    return _("main window");
}

/// Override to return the label for the control.
wxString AxFrameAdapter::GetLabel()
{
    if (GetFrame())
        return GetFrame()->GetTitle();
    else
        return wxEmptyString;
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxFrameAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetFrame())
    {
        wxString label = GetLabel();
        if (!label.IsEmpty())
            text << label << wxT(".");

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            text << wxT(" ") << content;
        }
    }
    return text;
}

// Get content
wxString AxFrameAdapter::GetContent(AxContentLevel level)
{
    wxString text;
    wxFrame* frame = GetFrame();
    if (frame)
    {
        // Get a summary of the frame.
        if (level == AxContentFull)
        {
            text << _("These are the window controls:") << wxT(" ");

            wxWindowList::compatibility_iterator node = frame->GetChildren().GetFirst();
            while (node)
            {
                wxWindow *child = node->GetData();
                DescribeWindowRecursively(child, AxContentSummary, text);

                node = node->GetNext();
            }

            // TODO: menubar, status line.
        }
    }
    return text;
}

wxFrame* AxFrameAdapter::GetFrame() const
{
    return wxDynamicCast(GetWindow(), wxFrame);
}

void AxFrameAdapter::OnMenuOpen(wxMenuEvent& event)
{
    if (GetFrame() && GetFrame()->GetMenuBar())
    {
        wxMenu* menu = event.GetMenu();
        menu->UpdateUI();

        wxString menuLabel;
        size_t i;
        int menuPosition = -1;
        for (i = 0; i < GetFrame()->GetMenuBar()->GetMenuCount(); i++)
        {
            if (menu == GetFrame()->GetMenuBar()->GetMenu(i))
            {
                menuPosition = i;
            }
        }

        // Don't immediately say this, because we're likely to get a menu highlight
        // event very soon, and that would stop the speech and prevent us hearing about
        // the menu open event. A better approach might be to allow current speech
        // to continue before the next one happens, in special circumstances. On
        // the other hand, it would be hard to know which event caused the speech.

        m_openMenuPending = menuPosition;
        if (m_timer.IsRunning())
            m_timer.Stop();
        m_timer.Start(500, true /* one shot */);
    }
    event.Skip();
}

void AxFrameAdapter::OnMenuClose(wxMenuEvent& event)
{
    if (GetFrame() && GetFrame()->GetMenuBar())
    {
        wxMenu* menu = event.GetMenu();
        wxString menuLabel;
        size_t i;
        for (i = 0; i < GetFrame()->GetMenuBar()->GetMenuCount(); i++)
        {
            if (menu == GetFrame()->GetMenuBar()->GetMenu(i))
            {
                menuLabel = GetFrame()->GetMenuBar()->GetMenuLabel(i);
            }
        }
        menuLabel = wxStripMenuCodes(menuLabel);
        if (!menuLabel.IsEmpty())
        {
            wxString text;
            text << wxT("Close menu: ");
            text << menuLabel << wxT(".");

            // On Windows at least, we need to do this immediately,
            // because opening the window goes into a loop and idle events
            // are not processed.
            Say(text, AxSpeechFlagsPurge|AxSpeechFlagsImmediate);
        }
    }

    event.Skip();
}

void AxFrameAdapter::OnMenuHighlight(wxMenuEvent& event)
{
    if (GetFrame() && GetFrame()->GetMenuBar())
    {
        wxMenuItem* menuItem = GetFrame()->GetMenuBar()->FindItem(event.GetId());
        if (menuItem)
        {
            wxString text;

            // Deal with a pending open menu request, which have been generated a short time
            // ago. Open and highlight events can happen almost simultaneously and we don't
            // wish to overwrite the open menu event.
            if (m_openMenuPending != -1 && GetFrame() && GetFrame()->GetMenuBar())
            {
                wxString menuLabel = GetFrame()->GetMenuBar()->GetMenuLabel(m_openMenuPending);
                m_openMenuPending = -1;
                m_timer.Stop();
                
                menuLabel = wxStripMenuCodes(menuLabel);
                if (!menuLabel.IsEmpty())
                {
                    text << wxT("Open menu, ");
                    text << menuLabel << wxT(".");
                }
            }

            wxString label = menuItem->GetItemLabel();
            label = wxStripMenuCodes(label);

            if (!label.IsEmpty())
            {
                text << wxT(" Menu item: ");
                text << label << wxT(".");

                // Find shortcut.
                if (GetShortcutManager() && GetShortcutManager()->FindById(menuItem->GetId()))
                {
                    wxShortcutItem* item = GetShortcutManager()->FindById(menuItem->GetId());
                    if (item->IsEnabled())
                    {
                        wxString shortcutText = GetShortcutManager()->GetReadableShortcutString(* item);
                        text << wxT(" ") << shortcutText << wxT(".");
                    }
                }
                else
                {
                    if (menuItem->GetItemLabel().Find(wxT("\t")) != wxNOT_FOUND)
                    {
                        wxString shortcutText = menuItem->GetItemLabel().AfterFirst('\t');
                        shortcutText.Replace(_("Ctrl"), _("Control"));
                        text << wxT(" ") << shortcutText << wxT(".");
                    }
                }

                if (!GetFrame()->GetMenuBar()->IsEnabled(menuItem->GetId()))
                {
                    text << wxT(" ") << _("Disabled.");
                }

                if (menuItem->IsCheckable())
                {
                    if (menuItem->IsChecked())
                        text << wxT(" ") << wxT("Checked.");
                    else
                        text << wxT(" ") << wxT("Not checked.");
                }
                
                // On Windows at least, we need to do this immediately,
                // because opening the window goes into a loop and idle events
                // are not processed.
                
                Say(text, AxSpeechFlagsPurge|AxSpeechFlagsImmediate);
            }
        }
    }
    event.Skip();
}

void AxFrameAdapter::OnMenuSelect(wxCommandEvent& event)
{
    if (GetFrame() && GetFrame()->GetMenuBar())
    {
        // When we select a command, we don't need to hear about a menu being opened
        // now.
        if (m_openMenuPending != -1)
        {
            m_openMenuPending = -1;
            m_timer.Stop();
        }

        wxMenuItem* menuItem = GetFrame()->GetMenuBar()->FindItem(event.GetId());
        if (menuItem)
        {
            wxString label = menuItem->GetItemLabel();
            label = wxStripMenuCodes(label);

            if (!label.IsEmpty())
            {
                wxString text;
                text << wxT("Menu command: ");
                text << label << wxT(".");
                
                // On Windows at least, we need to do this immediately,
                // because opening the window goes into a loop and idle events
                // are not processed.
                Say(text, AxSpeechFlagsPurge|AxSpeechFlagsImmediate);
            }
        }
    }
    event.Skip();
}

void AxFrameAdapter::OnTimeOut(wxTimerEvent& WXUNUSED(event))
{
    if (m_openMenuPending != -1 && GetFrame() && GetFrame()->GetMenuBar())
    {
        wxString menuLabel = GetFrame()->GetMenuBar()->GetMenuLabel(m_openMenuPending);
        m_openMenuPending = -1;

        menuLabel = wxStripMenuCodes(menuLabel);
        if (!menuLabel.IsEmpty())
        {
            wxString text;
            text << wxT("Open menu, ");
            text << menuLabel << wxT(".");

            // On Windows at least, we need to do this immediately,
            // because opening the window goes into a loop and idle events
            // are not processed.

            Say(text, AxSpeechFlagsPurge|AxSpeechFlagsImmediate);
        }
    }
    else
        m_openMenuPending = -1;
}

// Apply window colours
bool AxFrameAdapter::ApplyWindowColours()
{
    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxStaticTextAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxStaticTextAdapter, AxAdapter)
END_EVENT_TABLE()

wxStaticText* AxStaticTextAdapter::GetStaticText() const
{
    return wxDynamicCast(GetWindow(), wxStaticText);
}

// Apply window colours
bool AxStaticTextAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS( AxStaticBoxAdapter, AxStaticTextAdapter )

IMPLEMENT_DYNAMIC_CLASS(AxButtonAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxButtonAdapter, AxAdapter)
    EVT_BUTTON(wxID_ANY, AxButtonAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxButtonAdapter::GetName()
{
    if (GetBitmapButton())
        return _("bitmap button");
    else
        return _("button");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxButtonAdapter::GetStateText(AxContentLevel WXUNUSED(level))
{
    wxString text;
    if (GetButton())
    {
        wxString label = GetLabel();
        text << wxStripMenuCodes(label);
    }
    if (!GetWindow()->IsEnabled())
    {
        AddStop(text);
        text << wxT(" ") << _("not available.");
    }
    return text;
}

// Get content
wxString AxButtonAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    return wxEmptyString;
}

void AxButtonAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetButton() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(_("pressed button"));
    wxString label = wxStripMenuCodes(GetLabel());
    if (!label.IsEmpty())
        text << wxT(" ") << label;

    Say(text);
}

wxButton* AxButtonAdapter::GetButton() const
{
    return wxDynamicCast(GetWindow(), wxButton);
}

wxBitmapButton* AxButtonAdapter::GetBitmapButton() const
{
    return wxDynamicCast(GetWindow(), wxBitmapButton);
}

/// Update the appearance - text size, background, etc.
bool AxButtonAdapter::UpdateAppearance(int hints)
{
    if (AxViewManager::Get() && GetButton() && !GetBitmapButton())
    {
        if (hints & AxVisualSettingTextSize)
        {
            ApplyFontSize();

            wxSize sz = GetButton()->GetSize();
            wxSize bestSize = GetButton()->GetBestSize();
            GetButton()->SetSize(bestSize);
        }
        if (hints & (AxVisualSettingWindowColour|AxVisualSettingTextColour))
        {
            ApplyWindowColours();
        }
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxCheckBoxAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxCheckBoxAdapter, AxAdapter)
    EVT_CHECKBOX(wxID_ANY, AxCheckBoxAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxCheckBoxAdapter::GetName()
{
    return _("checkbox");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxCheckBoxAdapter::GetStateText(AxContentLevel WXUNUSED(level))
{
    wxString text;
    if (GetCheckBox())
    {
        text << wxStripMenuCodes(GetLabel());

        AddStop(text);

        text << wxT(" ");

        if (GetCheckBox()->Is3State() && GetCheckBox()->Get3StateValue())
        {
            text << _("undetermined state");
        }
        else if (GetCheckBox()->GetValue())
        {
            text << _("enabled");
        }
        else
        {
            text << _("disabled");
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);

            text << wxT(" ") << _("not available.");
        }
    }
    return text;
}

// Get content
wxString AxCheckBoxAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    return wxEmptyString;
}

void AxCheckBoxAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetCheckBox() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(_("pressed checkbox. "));
    text << GetStateText(AxContentSummary);

    Say(text);
}

wxCheckBox* AxCheckBoxAdapter::GetCheckBox() const
{
    return wxDynamicCast(GetWindow(), wxCheckBox);
}

// Apply window colours
bool AxCheckBoxAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxToggleButtonAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxToggleButtonAdapter, AxAdapter)
    EVT_CHECKBOX(wxID_ANY, AxToggleButtonAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxToggleButtonAdapter::GetName()
{
    return _("toggle button");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxToggleButtonAdapter::GetStateText(AxContentLevel WXUNUSED(level))
{
    wxString text;
    if (GetToggleButton())
    {
        text << wxStripMenuCodes(GetLabel());

        AddStop(text);

        text << wxT(" ");

        if (GetToggleButton()->GetValue())
        {
            text << _("enabled");
        }
        else
        {
            text << _("disabled");
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);

            text << wxT(" ") << _("not available.");
        }
    }
    return text;
}

// Get content
wxString AxToggleButtonAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    return wxEmptyString;
}

void AxToggleButtonAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetToggleButton() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(_("pressed toggle button. "));
    text << GetStateText(AxContentSummary);

    Say(text);
}

wxToggleButton* AxToggleButtonAdapter::GetToggleButton() const
{
    return wxDynamicCast(GetWindow(), wxToggleButton);
}

IMPLEMENT_DYNAMIC_CLASS(AxRadioButtonAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxRadioButtonAdapter, AxAdapter)
    EVT_RADIOBUTTON(wxID_ANY, AxRadioButtonAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxRadioButtonAdapter::GetName()
{
    return _("radio button");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxRadioButtonAdapter::GetStateText(AxContentLevel WXUNUSED(level))
{
    wxString text;
    if (GetRadioButton())
    {
        text << wxStripMenuCodes(GetLabel());

        AddStop(text);
        text << wxT(" ");

        if (GetRadioButton()->GetValue())
        {
            text << _("enabled");
        }
        else
        {
            text << _("disabled");
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }
    return text;
}

// Get content
wxString AxRadioButtonAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    return wxEmptyString;
}

void AxRadioButtonAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetRadioButton() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(_("pressed radio button"));
    text << wxT(".");
    text << wxT(" ") << GetStateText(AxContentSummary);

    Say(text);
}

wxRadioButton* AxRadioButtonAdapter::GetRadioButton() const
{
    return wxDynamicCast(GetWindow(), wxRadioButton);
}

// Apply window colours
bool AxRadioButtonAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxListBoxAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxListBoxAdapter, AxAdapter)
    EVT_LISTBOX(wxID_ANY, AxListBoxAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxListBoxAdapter::GetName()
{
    if (GetCheckListBox())
        return _("check list box");
    else
        return _("list box");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxListBoxAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetListBox())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxListBoxAdapter::GetContent(AxContentLevel level)
{
    wxListBox* listBox = GetListBox();
    wxCheckListBox* checkListBox = GetCheckListBox();
    wxString text;

    if (listBox)
    {
        if (listBox->GetCount() == 0)
            text << wxString::Format(_("There are no items."));
        else if (listBox->GetCount() == 1)
            text << wxString::Format(_("There is 1 item."));
        else
            text << wxString::Format(_("There are %d items."), (int) listBox->GetCount());

        if (level == AxContentFull)
        {
            size_t i;
            for (i = 0; i < listBox->GetCount(); i++)
            {
                text << wxT(" ") << (int) (i+1) << wxT(". ");
                wxString itemString = listBox->GetString(i);
                text << itemString;
                AddPause(text);

                if (checkListBox)
                {
                    if (checkListBox->IsChecked(i))
                        text << wxT(" ") << _("Checked.");
                    else
                        text << wxT(" ") << _("Not checked.");
                }

                if (listBox->IsSelected(i))
                {
                    text << wxT(" ") << _("Selected.");
                }
            }
        }
        else if ((listBox->GetWindowStyle() & wxLB_MULTIPLE) == 0)
        {
            int sel = listBox->GetSelection();
            if (sel != wxNOT_FOUND)
            {
                wxString itemString = listBox->GetString(sel);
                text << wxT(" ") << wxString::Format(_("Selection is %d: %s"), sel, itemString.c_str()) << wxT(".");
            }
        }
    }

    return text;
}

void AxListBoxAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetListBox() || (event.GetId() != GetWindow()->GetId()))
        return;

    int sel = event.GetSelection();
    if (sel != wxNOT_FOUND)
    {
        wxString text(wxString::Format(_("selected item %d"), (int) (sel+1)));
#if 0
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << wxT(" ") << label;
#endif
        
        Say(text);
    }
}

wxListBox* AxListBoxAdapter::GetListBox() const
{
    return wxDynamicCast(GetWindow(), wxListBox);
}

wxCheckListBox* AxListBoxAdapter::GetCheckListBox() const
{
    return wxDynamicCast(GetWindow(), wxCheckListBox);
}

IMPLEMENT_DYNAMIC_CLASS(AxListCtrlAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxListCtrlAdapter, AxAdapter)
    EVT_LIST_ITEM_SELECTED(wxID_ANY, AxListCtrlAdapter::OnSelection)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxListCtrlAdapter::GetName()
{
    return _("list control");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxListCtrlAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetListCtrl())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxListCtrlAdapter::GetContent(AxContentLevel level)
{
    wxListCtrl* listCtrl = GetListCtrl();
    wxString text;

    if (listCtrl)
    {
        if (listCtrl->GetItemCount() == 0)
            text << wxString::Format(_("There are no items."));
        else if (listCtrl->GetItemCount() == 1)
            text << wxString::Format(_("There is 1 item."));
        else
            text << wxString::Format(_("There are %d items."), (int) listCtrl->GetItemCount());

        if (listCtrl->GetWindowStyle() & wxLC_REPORT)
        {
            if (level == AxContentFull)
            {
                long i;
                for (i = 0; i < listCtrl->GetItemCount(); i++)
                {
                    text << wxT(" ") << (int) (i+1) << wxT(".");
                    wxString itemString = listCtrl->GetItemText(i);
                    if (!itemString.IsEmpty())
                    {
                        text << wxT(" ") << itemString;
                    }
                    AddPause(text);
                    
                    int state = listCtrl->GetItemState(i, wxLIST_STATE_SELECTED);
                    if (state & wxLIST_STATE_SELECTED)
                    {
                        text << wxT(" ") << _("Selected.");
                    }
                }
            }
            else if ((listCtrl->GetWindowStyle() & wxLC_SINGLE_SEL) == 0)
            {
                long sel = listCtrl->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
                if (sel != wxNOT_FOUND)
                {
                    wxString itemString = listCtrl->GetItemText(sel);
                    if (!itemString.IsEmpty())
                        text << wxT(" ") << wxString::Format(_("Selection is %d: %s"), sel, itemString.c_str()) << wxT(".");
                }
            }
        }
    }

    return text;
}

void AxListCtrlAdapter::OnSelection(wxListEvent& event)
{
    event.Skip();

    if (!GetListCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    int sel = event.GetSelection();
    if (sel != wxNOT_FOUND)
    {
        wxString text(wxString::Format(_("selected item %d"), (int) (sel+1)));
#if 0
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << wxT(" ") << label;
#endif
        
        Say(text);
    }
}

wxListCtrl* AxListCtrlAdapter::GetListCtrl() const
{
    return wxDynamicCast(GetWindow(), wxListCtrl);
}

IMPLEMENT_DYNAMIC_CLASS(AxTreeCtrlAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxTreeCtrlAdapter, AxAdapter)
    EVT_TREE_SEL_CHANGED(wxID_ANY, AxTreeCtrlAdapter::OnSelection)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxTreeCtrlAdapter::GetName()
{
    return _("tree control");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxTreeCtrlAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetTreeCtrl())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxTreeCtrlAdapter::GetContent(AxContentLevel level)
{
    wxTreeCtrl* treeCtrl = GetTreeCtrl();
    wxString text;

    if (treeCtrl)
    {
        int itemCount = 0;
        if (treeCtrl->GetRootItem().IsOk())
            itemCount = treeCtrl->GetChildrenCount(treeCtrl->GetRootItem());

        if (itemCount == 0)
            text << wxString::Format(_("There are no items."));
        else if (itemCount == 1)
            text << wxString::Format(_("There is 1 top-level item."));
        else
            text << wxString::Format(_("There are %d top-level items."), itemCount);

        if (level == AxContentFull)
        {
            wxTreeItemId parentId = treeCtrl->GetRootItem();
            if (parentId.IsOk())
            {
                text << wxT(" ") << _("Top level items are:") << wxT(" ");
                wxTreeItemIdValue cookie;
                wxTreeItemId id = treeCtrl->GetFirstChild(parentId, cookie);
                while (id.IsOk())
                {
                    wxString itemString = treeCtrl->GetItemText(id);

                    if (!itemString.IsEmpty())
                    {
                        text << wxT(" ") << itemString;
                    }
                    AddPause(text);

                    if (treeCtrl->IsSelected(id))
                    {
                        text << wxT(" ") << _("Selected.");
                    }

                    id = treeCtrl->GetNextChild(parentId, cookie);
                }
            }
        }
        else
        {
            wxTreeItemId sel = treeCtrl->GetSelection();
            
            if (sel.IsOk())
            {
                wxString itemString = treeCtrl->GetItemText(sel);
                if (!itemString.IsEmpty())
                    text << wxT(" ") << wxString::Format(_("Selection is %s"), itemString.c_str()) << wxT(".");
            }
        }
    }

    return text;
}

void AxTreeCtrlAdapter::OnSelection(wxTreeEvent& event)
{
    event.Skip();

    if (!GetTreeCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxTreeItemId sel = event.GetItem();
    if (sel.IsOk())
    {
        wxString itemString = GetTreeCtrl()->GetItemText(sel);
        if (!itemString.IsEmpty())
        {
            wxString text(wxString::Format(_("selected item %s."), itemString.c_str()));        
            Say(text);
        }
    }
}

wxTreeCtrl* AxTreeCtrlAdapter::GetTreeCtrl() const
{
    return wxDynamicCast(GetWindow(), wxTreeCtrl);
}

IMPLEMENT_DYNAMIC_CLASS(AxComboBoxAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxComboBoxAdapter, AxAdapter)
    EVT_COMBOBOX(wxID_ANY, AxComboBoxAdapter::OnCommand)
    EVT_TEXT(wxID_ANY, AxComboBoxAdapter::OnText)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxComboBoxAdapter::GetName()
{
    return _("combobox");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxComboBoxAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetComboBox())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxComboBoxAdapter::GetContent(AxContentLevel level)
{
    wxComboBox* comboBox = GetComboBox();
    wxString text;

    if (comboBox)
    {
        if (comboBox->GetCount() == 0)
            text << wxString::Format(_("There are no items."));
        else if (comboBox->GetCount() == 1)
            text << wxString::Format(_("There is 1 item."));
        else
            text << wxString::Format(_("There are %d items."), (int) comboBox->GetCount());

        int sel = comboBox->GetSelection();

        if (level == AxContentFull)
        {
            size_t i;
            for (i = 0; i < comboBox->GetCount(); i++)
            {
                text << wxT(" ") << (int) (i+1) << wxT(". ");
                wxString itemString = comboBox->GetString(i);
                text << itemString;
                AddPause(text);

                if ((int) i == sel)
                {
                    text << wxT(" ") << _("Selected.");
                }
            }
        }
        else
        {
            wxString value = comboBox->GetValue();
            if (value.IsEmpty())
            {
                text << wxT(" ") << wxString::Format(_("There is no value."));
            }
            else
            {
                text << wxT(" ") << wxString::Format(_("The current value is %s"), value.c_str()) << wxT(".");
            }
        }
    }

    return text;
}

void AxComboBoxAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetComboBox() || (event.GetId() != GetWindow()->GetId()))
        return;

    int sel = event.GetSelection();
    if (sel != wxNOT_FOUND)
    {
        wxString stringSelection = GetComboBox()->GetString(sel);
        wxString text(wxString::Format(_("selected item %d, %s"), (int) (sel+1), stringSelection.c_str()));
        
        Say(text);
    }
}

void AxComboBoxAdapter::OnText(wxCommandEvent& event)
{
    event.Skip();

    if (!GetComboBox() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString stringValue = GetComboBox()->GetValue();

    wxString text(wxString::Format(_("changed value %s"), stringValue.c_str()));
    
    Say(text);
}

wxComboBox* AxComboBoxAdapter::GetComboBox() const
{
    return wxDynamicCast(GetWindow(), wxComboBox);
}

IMPLEMENT_DYNAMIC_CLASS(AxChoiceAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxChoiceAdapter, AxAdapter)
    EVT_COMBOBOX(wxID_ANY, AxChoiceAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxChoiceAdapter::GetName()
{
    return _("choice");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxChoiceAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetChoice())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxChoiceAdapter::GetContent(AxContentLevel level)
{
    wxChoice* choice = GetChoice();
    wxString text;

    if (choice)
    {
        if (choice->GetCount() == 0)
            text << wxString::Format(_("There are no items."));
        else if (choice->GetCount() == 1)
            text << wxString::Format(_("There is 1 item."));
        else
            text << wxString::Format(_("There are %d items."), (int) choice->GetCount());

        int sel = choice->GetSelection();

        if (level == AxContentFull)
        {
            size_t i;
            for (i = 0; i < choice->GetCount(); i++)
            {
                text << wxT(" ") << (int) (i+1) << wxT(". ");
                wxString itemString = choice->GetString(i);
                text << itemString;
                AddPause(text);

                if ((int) i == sel)
                {
                    text << wxT(" ") << _("Selected.");
                }
            }
        }
        else
        {
            int sel = choice->GetSelection();
            if (sel != wxNOT_FOUND)
            {
                wxString itemString = choice->GetString(sel);
                text << wxT(" ") << wxString::Format(_("Selection is %d: %s"), sel, itemString.c_str()) << wxT(".");
            }
        }
    }

    return text;
}

void AxChoiceAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetChoice() || (event.GetId() != GetWindow()->GetId()))
        return;

    int sel = event.GetSelection();
    if (sel != wxNOT_FOUND)
    {
        wxString stringSelection = GetChoice()->GetString(sel);
        wxString text(wxString::Format(_("selected item %d, %s"), (int) (sel+1), stringSelection.c_str()));
        
        Say(text);
    }
}

wxChoice* AxChoiceAdapter::GetChoice() const
{
    return wxDynamicCast(GetWindow(), wxChoice);
}

IMPLEMENT_DYNAMIC_CLASS(AxSpinCtrlAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxSpinCtrlAdapter, AxAdapter)
    EVT_SPINCTRL(wxID_ANY, AxSpinCtrlAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxSpinCtrlAdapter::GetName()
{
    return _("spin control");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxSpinCtrlAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetSpinCtrl())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        AddStop(text);
        text << wxT(" ");
        text << wxString::Format(_("A value between %d and %d."), GetSpinCtrl()->GetMin(), GetSpinCtrl()->GetMax());

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ");
            text << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxSpinCtrlAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    wxSpinCtrl* spinCtrl = GetSpinCtrl();
    wxString text;

    if (spinCtrl)
    {
        text << wxString::Format(_("The current value is %d."), spinCtrl->GetValue());
    }

    return text;
}

void AxSpinCtrlAdapter::OnCommand(wxSpinEvent& event)
{
    event.Skip();

    if (!GetSpinCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(wxString::Format(_("updated spin value to %d."), (int) GetSpinCtrl()->GetValue()));
    
    Say(text);
}

wxSpinCtrl* AxSpinCtrlAdapter::GetSpinCtrl() const
{
    return wxDynamicCast(GetWindow(), wxSpinCtrl);
}

IMPLEMENT_DYNAMIC_CLASS(AxSliderAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxSliderAdapter, AxAdapter)
    EVT_SLIDER(wxID_ANY, AxSliderAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxSliderAdapter::GetName()
{
    return _("slider");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxSliderAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetSlider())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        AddStop(text);
        text << wxT(" ");
        text << wxString::Format(_("A value between %d and %d."), GetSlider()->GetMin(), GetSlider()->GetMax());

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ");
            text << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxSliderAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    wxSlider* sliderCtrl = GetSlider();
    wxString text;

    if (sliderCtrl)
    {
        text << wxString::Format(_("The current value is %d."), sliderCtrl->GetValue());
    }

    return text;
}

void AxSliderAdapter::OnCommand(wxCommandEvent& event)
{
    event.Skip();

    if (!GetSlider() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString text(wxString::Format(_("updated slider value to %d."), (int) GetSlider()->GetValue()));
    
    Say(text);
}

wxSlider* AxSliderAdapter::GetSlider() const
{
    return wxDynamicCast(GetWindow(), wxSlider);
}

IMPLEMENT_DYNAMIC_CLASS(AxHtmlWindowAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxHtmlWindowAdapter, AxAdapter)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxHtmlWindowAdapter::GetName()
{
    return _("HTML window");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxHtmlWindowAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetHtmlWindow())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxHtmlWindowAdapter::GetContent(AxContentLevel level)
{
    wxHtmlWindow* htmlCtrl = GetHtmlWindow();
    wxString text;

    if (htmlCtrl)
    {
        wxString value = htmlCtrl->ToText();
        if (value.Length() > 1000 && level == AxContentSummary)
        {
            value = value.Left(1000);
            value << wxT(". ");
            value << wxT("(remaining text omitted)");
        }
        text << value;
    }

    return text;
}

wxHtmlWindow* AxHtmlWindowAdapter::GetHtmlWindow() const
{
    return wxDynamicCast(GetWindow(), wxHtmlWindow);
}

IMPLEMENT_DYNAMIC_CLASS(AxNotebookAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxNotebookAdapter, AxAdapter)
    EVT_NOTEBOOK_PAGE_CHANGED(wxID_ANY, AxNotebookAdapter::OnNotebookPageChanged)
    EVT_CHOICEBOOK_PAGE_CHANGED(wxID_ANY, AxNotebookAdapter::OnChoicebookPageChanged)
    EVT_LISTBOOK_PAGE_CHANGED(wxID_ANY, AxNotebookAdapter::OnListbookPageChanged)
    EVT_TREEBOOK_PAGE_CHANGED(wxID_ANY, AxNotebookAdapter::OnTreebookPageChanged)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxNotebookAdapter::GetName()
{
    return _("tab control");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxNotebookAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetNotebook())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxNotebookAdapter::GetContent(AxContentLevel level)
{
    wxBookCtrlBase* notebookCtrl = GetBookCtrl();
    wxString text;

    if (notebookCtrl)
    {
        if (level == AxContentFull)
        {
            text << _("These are the tabs:") << wxT(" ");
            size_t i;
            for (i = 0; i < notebookCtrl->GetPageCount(); i++)
            {
                wxString pageText = notebookCtrl->GetPageText(i);
                text << wxT(" ") << pageText;
                AddStop(text);
            }
        }
        int sel = notebookCtrl->GetSelection();
        if (sel != wxNOT_FOUND)
        {
            wxString selStr = notebookCtrl->GetPageText(sel);
            text << wxString::Format(_(" The current tab is %d, %s."), (int) sel+1, selStr.c_str());
        }
    }

    return text;
}

wxBookCtrlBase* AxNotebookAdapter::GetBookCtrl() const
{
    return wxDynamicCast(GetWindow(), wxBookCtrlBase);
}

wxNotebook* AxNotebookAdapter::GetNotebook() const
{
    return wxDynamicCast(GetWindow(), wxNotebook);
}

wxChoicebook* AxNotebookAdapter::GetChoicebook() const
{
    return wxDynamicCast(GetWindow(), wxChoicebook);
}

wxListbook* AxNotebookAdapter::GetListbook() const
{
    return wxDynamicCast(GetWindow(), wxListbook);
}

wxTreebook* AxNotebookAdapter::GetTreebook() const
{
    return wxDynamicCast(GetWindow(), wxTreebook);
}

void AxNotebookAdapter::OnPageChanged(int sel)
{
    wxString text;
    if (sel != wxNOT_FOUND)
    {
        wxString pageText = GetBookCtrl()->GetPageText(sel);
        if (!pageText.IsEmpty())
            text << wxString::Format(_("changed page to %s."), pageText.c_str());
    }
    if (!text.IsEmpty())
        Say(text);
}

void AxNotebookAdapter::OnNotebookPageChanged(wxNotebookEvent& event)
{
    event.Skip();
    if (!GetBookCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    OnPageChanged(event.GetSelection());
}

void AxNotebookAdapter::OnChoicebookPageChanged(wxChoicebookEvent& event)
{
    event.Skip();
    if (!GetBookCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    OnPageChanged(event.GetSelection());
}

void AxNotebookAdapter::OnListbookPageChanged(wxListbookEvent& event)
{
    event.Skip();
    if (!GetBookCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    OnPageChanged(event.GetSelection());
}

void AxNotebookAdapter::OnTreebookPageChanged(wxTreebookEvent& event)
{
    event.Skip();
    if (!GetBookCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    OnPageChanged(event.GetSelection());
}

// Apply window colours
bool AxNotebookAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            // Use panel background for notebook?
            GetWindow()->SetBackgroundColour(AxViewManager::Get()->GetVisualSettings().GetBestWindowColour());
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}

IMPLEMENT_DYNAMIC_CLASS(AxHyperlinkCtrlAdapter, AxAdapter)

BEGIN_EVENT_TABLE(AxHyperlinkCtrlAdapter, AxAdapter)
    EVT_HYPERLINK(wxID_ANY, AxHyperlinkCtrlAdapter::OnCommand)
END_EVENT_TABLE()

// Override to return the name of the control.
wxString AxHyperlinkCtrlAdapter::GetName()
{
   return _("web address");
}

// Override to return text representing the state of the control,
// for example the current value.
wxString AxHyperlinkCtrlAdapter::GetStateText(AxContentLevel level)
{
    wxString text;
    if (GetHyperlinkCtrl())
    {
        wxString label = wxStripMenuCodes(GetLabel());
        if (!label.IsEmpty())
            text << label;

        wxString content = GetContent(level);
        if (!content.IsEmpty())
        {
            AddStop(text);
            text << wxT(" ") << content;
        }

        if (!GetWindow()->IsEnabled())
        {
            AddStop(text);
            text << wxT(" ") << _("not available.");
        }
    }

    return text;
}

// Get content
wxString AxHyperlinkCtrlAdapter::GetContent(AxContentLevel WXUNUSED(level))
{
    wxHyperlinkCtrl* hyperlinkCtrl = GetHyperlinkCtrl();
    wxString text;

    if (hyperlinkCtrl)
    {
        wxString url = hyperlinkCtrl->GetURL();
        text << wxString::Format(_("The web address is %s."), url.c_str());
    }

    return text;
}

void AxHyperlinkCtrlAdapter::OnCommand(wxHyperlinkEvent& event)
{
    event.Skip();

    if (!GetHyperlinkCtrl() || (event.GetId() != GetWindow()->GetId()))
        return;

    wxString url = event.GetURL();
    wxString text(wxString::Format(_("activating web address %s."), url.c_str()));
    Say(text);
}

wxHyperlinkCtrl* AxHyperlinkCtrlAdapter::GetHyperlinkCtrl() const
{
    return wxDynamicCast(GetWindow(), wxHyperlinkCtrl);
}

// Apply window colours
bool AxHyperlinkCtrlAdapter::ApplyWindowColours()
{
    if (AxViewManager::Get())
    {
        if (GetWindow())
        {
            GetWindow()->SetForegroundColour(AxViewManager::Get()->GetVisualSettings().GetBestTextColour());
        }
    }

    return true;
}
